mIRC Home    About    Download    Register    News    Help

Print Thread
#267727 13/09/20 12:35 AM
Joined: Jan 2004
Posts: 2,127
maroon Offline OP
Hoopy frood
OP Offline
Hoopy frood
Joined: Jan 2004
Posts: 2,127
A couple issues with $base rounding fractions:

(1) $base doesn't correctly handle fractions longer than 6 digits, if rounding to 6 digits causes it to round up to the next higher integer.

//var %i 1 , %a | while (%i isnum 1-99) { var %a %a $base(%i $+ .9999995,10,10,3) | inc %i } | echo -a %a

result:
001.999999 002.999999 003.999999 004.A 005.A 006.A 007.A 008.A 009.A 010.A 011.A 012.A 013.A 014.A 015.A 016.999999 017.999999 018.999999 019.999999 020.999999 021.999999 022.999999 023.999999 024.999999 025.999999 026.999999 027.999999 028.999999 029.999999 030.999999 031.999999 032.A 033.A 034.A 035.A 036.A 037.A 038.A 039.A 040.A 041.A 042.A 043.A 044.A 045.A 046.A 047.A 048.A 049.A 050.A 051.A 052.A 053.A 054.A 055.A 056.A 057.A 058.A 059.A 060.A 061.A 062.A 063.A 064.A 065.A 066.A 067.A 068.A 069.A 070.A 071.A 072.A 073.A 074.A 075.A 076.A 077.A 078.A 079.A 080.A 081.A 082.A 083.A 084.A 085.A 086.A 087.A 088.A 089.A 090.A 091.A 092.A 093.A 094.A 095.A 096.A 097.A 098.A 099.A

Some of those numbers rounded down 'correctly', but that's because of the known float rounding bug that's seen in $round where some numbers incorrectly round the '5' down instead of up. If that last '5' digit is changed to be anything in [6789] then all the above fractions will be '.A'

//var %i .05 , %a | while (%i < 99) { var %a %a $round(%i,1) | inc %i } | echo -a %a
result: 0.1 1.1 2 3 4 5 6 7 8.1 9.1 10.1 11.1 12.1 13.1 14.1 15.1 16.1 17.1 18.1 19.1 20.1 21.1 22.1 23.1 24.1 25.1 26.1 27.1 28.1 29.1 30.1 31.1 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98

//var -s %price 1.00 , %tax .095 , %total %price + %tax | echo -a %price + tax %tax = $ $+ $round(%total,2)
result: 1.00 + tax .095 = $1.09

(2) At higher outbase, $base also shortens the fraction to fewer digits than needed.

//var -s %a $base(9.999999,10,11) , %b $base(%a,11,10)
result: 9.999999
//var -s %a $base(9.999999,10,36) , %b $base(%a,36,10)
result: 9

//echo -a $calc( $log($calc(10^6)) / $log(2) ) vs $calc( $log($calc(36^3)) / $log(2) )
result: 19.931574 vs 15.509779

The above shows that 6 base10 digits fits within 20 bits, while 3 base36 digits fits within 16 bits, yet base36 won't output fractions longer than 2 digits.

The base-X fraction should be long enough to translate a fraction from base-10 to base-X, then translate back to a base-10 fraction reasonably close to the original fraction.

//var %out 36 , %a $base($pi,10,%out) , %b $base(%a,%out,10) | echo -a pi is %b
result: pi is 3.13966

Bases up through 28 will output 6 digit fractions, at which point it requires 29 bits to hold that fraction. Above base28, the length drastically drops until bases 35-36 will not output fractions longer than 2 digits.

//var %i 1.654321 , %out 36 , %a 0 0 0 0 0 0 0 | while (%i < 10) { var %n $len($gettok($base(%i,10,%out),2,46)) + 1 , %t 1 + $gettok(%a,%n,32) , %a $puttok(%a,%t,%n,32) | inc %i .01 } | echo -a %out : $regsubex(foo,%a,/(\d+)/g,$+($calc(\n -1),:,\t))
result:
28 : 0:0 1:0 2:0 3:0 4:0 5:33 6:802
29 : 0:0 1:0 2:0 3:9 4:25 5:801 6:0
30 : 0:0 1:0 2:0 3:0 4:0 5:835 6:0
31 : 0:0 1:0 2:0 3:33 4:802 5:0 6:0
32 : 0:0 1:0 2:0 3:33 4:802 5:0 6:0
33 : 0:0 1:0 2:26 3:809 4:0 5:0 6:0
34 : 0:0 1:0 2:34 3:801 4:0 5:0 6:0
35 : 0:8 1:33 2:794 3:0 4:0 5:0 6:0
36 : 0:8 1:25 2:802 3:0 4:0 5:0 6:0

Joined: Dec 2002
Posts: 5,411
Hoopy frood
Offline
Hoopy frood
Joined: Dec 2002
Posts: 5,411
Thanks for your bug report. The quirks of floating point calculations have been discussed many times.

See here and here and here for an explanation.

This means that when rounding, for example, N.5, where N is a value, it will for some Ns round up and for others round down.

Apart from that, $base() involves calls to combinations of mod(), round(), pow(), among other calls, so the above behaviour will be compounded with other rounding errors.

Note that if you are using floating point in $base(), there is no guarantee that you will be able to convert back and forth between results. Not all floating point values in one base can be represented in a different base.

As for decimal precision being shortened as the base is increased: ideally, the scripters who requested this would comment at this point, however as it was implemented 17 years ago... I can only assume it was due to issues with precision, overflow, etc. The $base() code has been tweaked so many times since then that this limitation may not even be needed any more.

I am going to remove the precision limit on large bases in the next beta but this will need testing.

Joined: Jan 2004
Posts: 2,127
maroon Offline OP
Hoopy frood
OP Offline
Hoopy frood
Joined: Jan 2004
Posts: 2,127
My (1) report was not about the existence of the float rounding error, which I said was a known issue, but was about fractions output using a character outside the characters of that base's alphabet. I know that $base has always allowed inputs for all bases to contain any character from the base36 alphabet, so $base(Q4,16,10) returned 26*16+4=420. However, is it also intended design that outputs also return a character that doesn't 'belong' in that specific outbase?

When I pointed out that $base(9.9999999,10,10) was returning 9.A instead of 10, I didn't realize it would eventually return 10, but only if there were at least 15 repeating 9's.

In general, when the fraction repeats that base's highest digit often enough, the $base output changes from valid-style output into having output consisting only of that single invalid digit. For some bases, but not all, repeating that fraction digit often enough eventually changes the output to be the next higher integer without a fraction. The exception is base36, since there is no next-higher-digit for that alphabet. When the Z.ZZ fraction repeats 5-9 times, the fraction changes from Z.ZZ-something into having no fraction at all, effectively decreasing the result by 1 down to 'Z', until the fraction being Z-repeated-ten-times changes the output to be '10'.

//echo -a $base(9.999999,10,10) $base(9.9999999,10,10) $base(9.999999999999999,10,10) -> 9.999999 9.A 10
//echo -a $base(Z.ZZZZ,36,36) $base(Z.ZZZZZ,36,36) $base(Z.ZZZZZZZZZZ,36,36) -> Z.ZZZYBJ Z 10

Base as low as 2 requires length 53 fraction before it changes from 1.2 to 10, while other bases like 12 never change from B.C to 10. My question isn't whether or not it should transition from 'something' to 10 since that's what $int(9.99) does with enough 9's tacked on, but whether the 'something' output should contain a character outside that base's alphabet.

//var -s %base $r(2,36) , %i 1 , %digit $base($calc(%base -1),10,%base) | while (%i isnum 1-53) { var %a $+(%digit,.,$str(%digit,%i)) , %aa $base(%a,%base,%base) | echo -a %i : %a -> %aa | inc %i | if (%aa == 10) break }

Joined: Dec 2002
Posts: 5,411
Hoopy frood
Offline
Hoopy frood
Joined: Dec 2002
Posts: 5,411
Quote
My (1) report was not about the existence of the float rounding error, which I said was a known issue, but was about fractions output using a character outside the characters of that base's alphabet. I know that $base has always allowed inputs for all bases to contain any character from the base36 alphabet, so $base(Q4,16,10) returned 26*16+4=420. However, is it also intended design that outputs also return a character that doesn't 'belong' in that specific outbase?

Yes, as it is allowing invalid characters as input, this will result in invalid characters as output.

Other than that, is the current beta doing what you expect? If not, please provide a script with a set of test cases the show the exact output you want from it.

Should I revert the change to the limit on precision length on larger bases, which had been in place for a long time, or not?

Last edited by Khaled; 14/09/20 07:03 PM.
Joined: Jan 2004
Posts: 2,127
maroon Offline OP
Hoopy frood
OP Offline
Hoopy frood
Joined: Jan 2004
Posts: 2,127
My last example of that post was the test case, where output as base10 can contain 'A', and hex output contains 'G'.

//echo -a $base(F.FFFFFF,16,16) or $base(F.FFFFFF,16,10) -> F.G or 15.A

I see that a long enough fraction eventually returns the next higher integer, but I wouldn't expect things like the hex F.G or the non-numeric 15.A

Edit: I'm not yet seeing any problem with allowing longer fractions at the higher bases. If it does eventually become a problem, it doesn't need to be truncated as severely as it has been. $base(999999,10,36) is a 4 digit output, so chopping the base36 fraction to 2 digits was just being too short. By allowing longer fractions in the beta, the last example using base36 no longer returns 'Z' for inputs like $base(Z.ZZZZ,36,36). But if the F.G and 15.A is by-design, then OK.

Last edited by maroon; 14/09/20 07:25 PM.
Joined: Dec 2002
Posts: 5,411
Hoopy frood
Offline
Hoopy frood
Joined: Dec 2002
Posts: 5,411
Right, so in both cases, there is no exact representation of that value in floating point so it is eventually rounded up.

I am going to add a check for the case where the fraction calculation rounds up leading to an increment in the integer value, so in your examples, the results would end up as "10" and "16".

Would this resolve the issue you are seeing?

Joined: Jan 2004
Posts: 2,127
maroon Offline OP
Hoopy frood
OP Offline
Hoopy frood
Joined: Jan 2004
Posts: 2,127
yes, thanks


Link Copied to Clipboard