in reply to int() function
It prints "Number is: 196, Integer part is: 195", but I would expect $x2 to be 196
Your expectation is unreasonable. (I know ... it seems like a reasonable expectation ... but it is not.)
First of all, note that (1.15*170)+0.50 cannot be exactly represented in a double.
This is mainly due to the fact that 1.15 cannot be exactly represented in a double.
When you assign a value of 1.15 to a double you are actually assigning the precise value 1.149999999999999911182158029987476766109466552734375.
And the expression (1.15*150)+0.50 evaluates to exactly 195.999999999999971578290569595992565155029296875.
Perl's print() function takes the action of rounding that value to 15 decimal digits. It does that for all floating point values  always to 15 decimal digits (unless perl's nvtype is other than "double") and always for a dubious reason.
So ... quite clearly, the integer portion of 195.999999999999971578290569595992565155029296875 is 195, and 195.999999999999971578290569595992565155029296875 rounded to 15 decimal places is 196.
And this agrees with the results that you obtained.
It leaves shaking my head in disbelief, too ... but that's the stupid way that perl's print() function has been doing it for a long time and, unfortunately, I can't see it doing anything differently any time soon.
A far saner approach would be for perl's print() function to have displayed the value 195.99999999999997 as happens with python3 and raku.
Note that, although you have questioned the printed value of $x2, it's actually the printed value of $x1 that's misleading.
Cheers, Rob
Re^2: int() function
by tobyink (Canon) on Oct 24, 2020 at 20:04 UTC

Minor correction: in OP's code, it's not Perl's print() function that is rounding the floating point value up to 196. It's happening on string interpolation.
 [reply] 

I think it's anything which does a stringification.
DB<137> $x1 = (1.15*170)+0.50;
DB<138> p "$x1"
196
DB<139> p $x1
196
DB<140> printf "%.20f\n",$x1
195.99999999999997000000
DB<141> $str = $x1.""
DB<142> p length $str
3
DB<143>
yep!
DB<151> $x2 = (1.15*170)+0.50;
DB<152> Dump $x2
SV = NV(0x32cc698) at 0x32cc6b0
REFCNT = 1
FLAGS = (NOK,pNOK)
NV = 196
DB<153> p $x2
196
DB<154> Dump $x2
SV = PVNV(0x1cb938) at 0x32cc6b0
REFCNT = 1
FLAGS = (NOK,pNOK)
IV = 0
NV = 196
PV = 0x31a4748 "196"\0
CUR = 3
LEN = 32
Cheers Rolf
_{(addicted to the Perl Programming Language :)
Wikisyntax for the Monastery
}
 [reply] [d/l] [select] 
Re^2: int() function
by LanX (Cardinal) on Oct 24, 2020 at 16:05 UTC

> It does that for all floating point values  always to 15 decimal digits (unless perl's nvtype is other than "double") and always for a dubious reason.
The mantissa of a double has 52 bits°, you need 50 bits to encode 15 decimal digits ( 2**10 =1024, so rule of thumb 10 bits for 3 decimals)
So the last two bits are used for error correction, since they don't suffice to encode another decimal digit.
Is it a good solution? Well I'd say appropriate for the era and not "for a dubious reason".
Are there better approaches? Sure, but they are harder to implement and would have been quite slow back in the days.
From my experience, >95% of the problems arise from calculating with currencies and the solution is obvious, calculate with integer cents and move the point only for output.
Cheers Rolf
_{(addicted to the Perl Programming Language :)
Wikisyntax for the Monastery
}
°) WP actually says 53 by using a spare bit redundancy , but tl;dr and this still fails to encode 16 decimals.  [reply] 

Well I'd say appropriate for the era and not "for a dubious reason".
I probably should have said "for lack of a sound reason". (Hmmm ... not sure if that's any different ;)
It's just that, if the stringification provided an extra 2 decimal digits of precision, we would avoid having to look at rubbish diagnoses like this one (where the test fails but "got" and "expected" are reported as being the same):
C:\_32>perl MTest::More le "cmp_ok(0.14, '==', 1.4 / 10, '0.14 == 1.
+4/10'); done
_testing();"
not ok 1  0.14 == 1.4/10
# Failed test '0.14 == 0.14'
# at e line 1.
# got: 0.14
# expected: 0.14
1..1
# Looks like you failed 1 test of 1.
IMO, if $x is an NV, then the condition "$x" == $x should always be true unless $x is NaN.
And that's the way it would be if doubles stringified to 17 digits of precision instead of the current 15 digits.
I would regard that as being a significant improvement for very little cost.
And we would then see that "got" is 0.14000000000000001 and "expected" is 0.13999999999999999  which at least makes some sense.
It's still not ideal because the strings "0.14000000000000001" and "0.14" both assign to the same double  so why print out all of those extra digits ?
Python3 (and Raku, I believe) use as few digits as are needed and would report the double 0.14 as being "0.14" and not "0.14000000000000001".
I've implemented that Python3/Raku behaviour in Math::MPFR  though with a different algorithm and probably not as efficiently as Python3/Raku.
C:\>perl MMath::MPFR="nvtoa" le "print nvtoa(0.14);print nvtoa(1.4 /
+ 10);"
0.14
0.13999999999999999
Cheers, Rob  [reply] [d/l] [select] 

> if the stringification provided an extra 2 decimal digits
How this? There are only 23 bits left and 2**3 < 10, so no way to get a 16th decimal out of a double.
I know it's confusing. But if you stuff a number with more than 15 decimals into a double you'll have loss anyway. And I doubt it's better in JS or python.
>
"cmp_ok(0.14, '==', 1.4 / 10
Well, but on the other hand eq should work because of the magic.
IMHO most people understand rounding errors, the point of confusion is that exact decimal fractions are not always exact binarie floats.
Probably we should have a shortcut for something like printf "%.18f" to facilitate diagnosis.
Cheers Rolf
_{(addicted to the Perl Programming Language :)
Wikisyntax for the Monastery
}
 [reply] [d/l] [select] 




It has 53 bits of precision because there's an implied leading 1 bit that's not stored.
log10(2^53) ~ 15.95, not quite 16. So not exactly 16 decimal digits of precision, but damn close. What's important here is that it's more than 15.
$ perl e'
printf "%.16f\n", 0.1234567890123456;
printf "%.16f\n", 0.1234567890123457;
'
0.1234567890123456
0.1234567890123457
 [reply] [d/l] [select] 
Re^2: int() function
by geoperl (Novice) on Oct 24, 2020 at 22:28 UTC

Thank you Rob
You are right. In my first example (1.15*170)+.5 the 15th decinal digit is 4 so it rounds to 195, whereas in my second example (1.15*150)+.5 the 15th decinal digit is 6 so it rounds to 173.
The 1.15 is actually 15% (selectable by user) on top of a price. So the script calculates (1+ 15/100)*price and because the final price must be rounded to the nearest integer, I added 0.50
I wanted to avoid loading Math::Round for just one calculation.
 [reply] 

 [reply] 

 [reply] 

