float x = 226.0;
if( ABS(x226.0)>0.00000001 ){ printf("ajaja\n"); }
What you've shown here is 226.0  226.0, which may or may not be the same thing as 2.26*100.0  226.0; it would have been a more meaningful deltacheck if you'd actually done the multiplication from the small float. However, when I tried the latter with the gcc that came with Strawberry, it still didn't show the "ajaja" message, so it's still closer than 0.00000001. That is because a c float is 32bit, so the 32bit ULP of 226.0 is 1.5259e5. But you showed:
perl V:nvsize V:nvtype
nvsize='8';
nvtype='double';
... so perl is currently using a c double for NV... which means your c code should have been using doubles to replicate it. (You might ask "why did it work on previous versions?" A version of 2.25 doesn't have the rounding problem, because it's exactly representable in a cdouble/perlNV. And the version before that was 2.24, and 2.24*100=224.00000000000003 as a double, so int(that) rounds down to 224, so you just hadn't noticed it on that version.)
The ULP_64bit_double(226.0) is 2.842e14, so checking against 1e8 is not sufficient. Updating the ccode to also show a double variable 2.26*100.0 and compare the error to ULP(226.0):
/* gcc a.c */
#include <stdio.h>
#define ABS(A) ((A)>0?(A):((A)))
int main(void){
float version = 2.26;
float newversion = version + 0.01;
printf("version=%f, newversion=%f\n", version, newversion);
/* bliako's "check" */
printf("2.26*100=%.8f\n", 2.26*100);
float x = 226.0;
if( ABS(x226.0)>0.00000001 ){ printf("bliako's check has ABS erro
+r\n"); } else {printf("bliako's check OK\n");}
/* closer check: check the multiplied value against the hardcoded
+226.0 */
/* as a 32bit float, the ULP(226.0) is (2**7)*(2**23) = 2**16 =
+ 15.259e6 = 1.5259e5, so a 1 ULP error would be >1.525e5, so also
+greater than 0.00000001 */
x = 2.26 * 100.0;
printf("2.26*100=%.16f\n", x);
if( ABS(x226.0)>0.00000001 ){ printf("floatULP check has ABS err
+or\n"); } else {printf("floatULP check OK\n");}
/* but perl with nvtype=double requires a check against a c double
+, where the ULP(226.0) = 2.842e14 */
double d = 2.26 * 100.0;
printf("2.26*100=%.16lf\n", d);
if( ABS(d226.0)>=2.842e14 ){ printf("doubleULP check has ABS er
+ror\n"); } else {printf("doubleULP check OK\n");}
}
gives output
version=2.260000, newversion=2.270000
2.26*100=226.00000000
bliako's check OK
2.26*100=226.0000000000000000
floatULP check OK
2.26*100=225.9999999999999700
doubleULP check has ABS error
You will notice that the double shows the same issue as the perl v5.36.0 which you showed. (Please note, as I said in chatterbox yesterday, that this is dependent on perl's compilation choices (nvsize/nvtype), not on v5.36.0; the Strawberry Perl v5.32.1 that I use also has double nvtype, so it shows the same behavior with 2.26*100.0 that your v5.36.0 shows.
So, the advice to use decimal cents to do calculations in cents instead of using floating dollars is sound but how do I get to the decimal cents in the first place?
Like I suggested in the chatterbox yesterday, one possible way is perl MPOSIX=round le "printf(qq(%.2f), (round(2.26 * 100.0) + 1.0)/100.0)"  this uses POSIX's round() function to roundtonearest after you've done the multiplication. Or, to put it another way, my $cents = POSIX::round(2.26*100);, then do the remaining calculations with the cents.
Or treat the number as a string, and, as syphilis implied, split the string on the decimal point, and just do the cents manipulation on the righthand side, after error checking and appropriate manipulation, depending on whether you mean `1.1` to be 1*100+1 or 1*100+10, and whether you want to carry from 1.99 to 2.00 or not.
