-2

I get data from an api request and perform a calculation based on the value.

Let say my bank account or crypto account have a balance of 301.38481999999999

I want to withdraw all of the money, but the float64 variable in golang automatically round up the variable to 301.38482 which caused the withdrawal operation to fail because I do not have that much money in my account.

package main

import(
    "log"
)

func main(){

    var myvar float64

    myvar = 301.38481999999999
    log.Println(myvar)
}

https://play.golang.org/p/BXk9fcVJZVn

It show

301.38482

How can I get the exact balance with exact number so that I can withdraw all of it?

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
dramasea
  • 3,370
  • 16
  • 49
  • 77
  • 6
    A float64 is an [IEEE 754-2008](https://en.wikipedia.org/wiki/Double-precision_floating-point_format) standard. Never use floating point for exact decimal values, like currency. – JimB Feb 03 '21 at 18:29
  • 2
    @JimB except that this is crypto, not dollars and cents, so fixed-point won't work. – Adrian Feb 03 '21 at 18:33
  • 3
    Then you have to work within the constraints of floating point arithmetic. How do you have a `float64` value of `301.38481999999999` when that value is not representable as a double precision float? If you need arbitrary precision floats, use `math/big`. – JimB Feb 03 '21 at 18:37
  • yeah it is crypto where i do not know how much is the decimal places – dramasea Feb 03 '21 at 18:37
  • 5
    "it is crypto" does not describe the types and data you are working with. To answer your exact question, the smallest subnormal double precision float is approximately 4.9406564584124654 × 10^−324, and the smallest normal positive double is approx 2.2250738585072014 × 10^−308, but this seems like an XY problem. – JimB Feb 03 '21 at 18:42
  • It seems like the actual problem here is that you're assuming what you see printed by `log.Println(myvar)` is the full precision of the value, which it isn't. [Per the docs](https://golang.org/pkg/fmt/), the default precision is 6 places. – Adrian Feb 03 '21 at 19:24
  • Package math contains the relevant data. – Volker Feb 03 '21 at 21:08
  • Whether you are dealing with traditional currency or cryptocurrency, it is denominated in some units. The units might nominally be “dollars” or “bitcoins,” but the actual unit used in transactions is some smaller indivisible unit like cents or Satoshi (ten billions of a bitcoin). Scale your measurements to be that smaller unit. Then both integer and floating-point types can represent that unit without error, up until the number of units exceeds their precision. – Eric Postpischil Feb 03 '21 at 23:37

1 Answers1

3

You're confusing the output you see with the actual value you have. If you check the docs of fmt:

For floating-point values, width sets the minimum width of the field and precision sets the number of places after the decimal, if appropriate, except that for %g/%G precision sets the maximum number of significant digits (trailing zeros are removed). For example, given 12.345 the format %6.3f prints 12.345 while %.3g prints 12.3. The default precision for %e, %f and %#g is 6; for %g it is the smallest number of digits necessary to identify the value uniquely.

fmt.Println always uses the default precision for whatever value you pass in. To see more precision, specify more precision:

var myvar float64
myvar = 301.38481999999999
log.Printf("%.20f", myvar)

// 301.38481999999999061401

https://play.golang.org/p/T4iyQcHY4qi

Regardless of what's printed, all internal arithmetic will be done with the full precision of the value. If you need more precision than float64 provides, see the math/big package.

Adrian
  • 42,911
  • 6
  • 107
  • 99
  • thank you so much for your answer. Is there away to print the string as it is (301.38481999999999) without knowing how much the floating point places? because the value are retreived from API and I might not know the exact floating places of the value – dramasea Feb 03 '21 at 19:38
  • 3
    No, because it's not a string. It's a decimal representation of a floating-point number, and not all decimal representations translate perfectly to binary representations; there will almost always be some inaccuracy in that conversion. – Adrian Feb 03 '21 at 19:46
  • 2
    If you're receiving it from the API as a string, you should keep it as a string rather than converting to float, unless you need to do math on it, in which case you'll have to contend with floating-point precision. – Adrian Feb 03 '21 at 19:50