will / crystal-pg

a postgres driver for crystal
BSD 3-Clause "New" or "Revised" License
462 stars 77 forks source link

getting OverflowError:Arithmetic overflow #222

Closed huuhait closed 3 years ago

huuhait commented 3 years ago

For example 0.33908 * 595.8 = 202.02386399999997 in Crystal lang if db saved a data like 202.02386399999997 with type numeric it'll working good but it can't read it again

https://github.com/will/crystal-pg/blob/master/src/pg/numeric.cr#L60

image

huuhait commented 3 years ago

Hope you will see this error

will commented 3 years ago

@huuhait Can you provide a numeric that has this problem? I tried the number in your text, then two of numbers in your screenshot, and haven't been able to reproduce it yet

require "pg"

def debug(ndigits, weight, sign, dscale, digits)
  n = PG::Numeric.new(ndigits.to_i16, weight.to_i16, sign.to_i16, dscale.to_i16, digits.map(&.to_i16))
  puts n
  puts n.inspect
  puts n.to_s
  puts n.to_f
  n
end

debug 2, -1, 0, 5, [3390, 8000]
debug 2, 0, 0, 1, [595, 8000]
debug 5, 0, 0, 14, [202, 238, 6399, 9999, 9700]
[0.33908, 2, -1, 0, 5, [3390, 8000]]
0.33908
0.33908
0.33908
0.33908
[595.8, 2, 0, 0, 1, [595, 8000]]
595.8
595.8
595.8
595.8
[202.02386399999997, 5, 0, 0, 14, [202, 238, 6399, 9999, 9700]]
202.02386399999997
202.02386399999997
202.02386399999997
202.02386399999997

To get the digits, weight, sign, dscale, and digits, one possible way is to add a print here

diff --git i/src/pg/numeric.cr w/src/pg/numeric.cr
index 19e3bcd..aebe970 100644
--- i/src/pg/numeric.cr
+++ w/src/pg/numeric.cr
@@ -34,6 +34,7 @@ module PG

     def initialize(@ndigits : Int16, @weight : Int16, sign, @dscale : Int16, @digits : Array(Int16))
       @sign = Sign.from_value(sign)
+      p [self, ndigits, weight, sign, dscale, digits]
     end

     # Returns `true` if the numeric is not a number.

and that should print before the exception. But also if you can get a number you know always does it from a select query, and can give it to me, I can try and figure it out.

will commented 3 years ago

For the above thing, you can either edit the code in lib as a temporary test, or in your own code after you require "pg" you could probably monkey patch over the numeric initializer

require "pg"
module PG
  struct Numeric
     def initialize(@ndigits : Int16, @weight : Int16, sign, @dscale : Int16, @digits : Array(Int16))
       @sign = Sign.from_value(sign)
       p [self, ndigits, weight, sign, dscale, digits] # maybe remove the self part if that also cause the problem
     end
  end
end

I haven't actually tested that, so there might be some mistakes

huuhait commented 3 years ago

@will i've tested here is results value in database is 2566.1918905000002000

image image Playground: https://play.crystal-lang.org/#/r/a763

will commented 3 years ago

Thank you, I can reproduce with that number.

Looking into postgres itself it seems like maybe how they go from numeric to float is to generate a string then parse that string, which seems wasteful, but maybe that's the only way for certain https://github.com/postgres/postgres/blob/32d6287d2eef6b6a4dde07e0513f3e4f321792ad/src/backend/utils/adt/numeric.c#L4306-L4330

Looking into different solutions

will commented 3 years ago

Thanks for reporting this. I expect to do a release sometime next week.