terraform icon indicating copy to clipboard operation
terraform copied to clipboard

String interpolation of variable produced by float subtraction doesn't work as expected

Open kimh opened this issue 6 years ago • 4 comments

Terraform Version

$ terraform version 
Terraform v0.12.10
+ provider.aws v2.30.0
+ provider.datadog v2.4.0
+ provider.template v2.1.2
+ provider.tls v2.1.1

Terraform Configuration Files

terraform {
  required_version = "= 0.12.10"
}

locals {
  good = 0.1
  also_good = 0.1 + 0.15
  bad = 0.25 - 0.15
}

output "bad" {
  value = "value: ${local.bad}"
}

output "good" {
  value = "value: ${local.good}"
}

output "also_good" {
  value = "value: ${local.also_good}"
}

Expected Behavior

I expect to get "value: 0.1" for output of bad.

Actual Behavior

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

also_good = value: 0.25
bad = value: 0.09999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
good = value: 0.1

As you can see, the value of bad is not correct.

Steps to Reproduce

  1. terraform init
  2. terraform apply

Additional Context

I can only reproduce this from terraform run. The same code in Terraform console is working as expected.

$ terraform console 
> "${0.25 - 0.15}"
0.1

kimh avatar Oct 17 '19 04:10 kimh

Hi @kimh, thank you for reporting this odd behavior. There appears to be something odd happening when terraform converts float 0.1 to a string.

If you remove the string interpolation, the expected result is outputted:


locals {
  good = 0.1
  also_good = 0.1 + 0.15
  bad = 0.25 - 0.15
}

output "bad" {
  value = local.bad
}
terraform apply

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:
bad = 0.1

ghost avatar Oct 17 '19 13:10 ghost

format(), on the other hand, can be used to format the string precisely:

output "bad" {
  value = format("%.1f", local.bad)
}
terraform apply

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

bad = 0.1

ghost avatar Oct 17 '19 14:10 ghost

I managed to work around this using tonumber(format("%.2f", local.thing)) because I didn't want to peg the precision to only 1 place

glenjamin avatar Oct 22 '19 21:10 glenjamin

As part of revisiting some older issues I've just re-confirmed this with a build from the v1.4 development branch (not actually a tagged release, but it's currently pre-beta at the time I'm writing this comment):

$ terraform apply

Changes to Outputs:
  + also_good = "value: 0.25"
  + bad       = "value: 0.09999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"
  + good      = "value: 0.1"

You can apply this plan to save these new output values to the Terraform state, without changing any real infrastructure.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes


Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

also_good = "value: 0.25"
bad = "value: 0.09999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"
good = "value: 0.1"

This sort of behavior is a classic problem with presenting floating-point values in decimal, because of course floating point numbers are naturally in binary and so some values we can write in decimal have no exact representation in binary. Therefore I don't know if we will actually be able to thoroughly solve this in all cases, but we'll leave this bug open to represent it in case we have a new idea for how to solve it later.

As previously discussed, you can use the built-in format function to give Terraform more explicit instructions about what level of precision you need if this issue causes problems for your configurations.

apparentlymart avatar Feb 03 '23 18:02 apparentlymart