terraform-plugin-framework icon indicating copy to clipboard operation
terraform-plugin-framework copied to clipboard

Provider produced invalid plan error when planning certain high-precision floating point numbers

Open SBGoods opened this issue 7 months ago • 0 comments

Problem Statement

When round-tripping certain floating point numbers such as MaxFloat32 from Terraform config to a type.Float32 or a type.Float64 framework type:

resource "framework_float64_precision" "test" {  
    float64_attribute = 340282346638528859811704183484516925440
}

Applying the above config results in the following error from Terraform:

╷
│ Error: Provider produced invalid plan
│ 
│ Provider "registry.terraform.io/hashicorp/scaffolding" planned an invalid value for scaffolding_float32_precision.test.float32_attribute: planned value
│ cty.NumberIntVal(3.4028234663852886e+38) does not match config value cty.NumberIntVal(3.4028234663852885981170418348451692544e+38).
│ 
│ This is a bug in the provider, which should be reported in the provider's own issue tracker.
╵

Context:

Floating point numbers in Terraform configuration are parsed in Terraform core as strings with 512 bit precision into a big.Float object. Most floating point numbers that are roundtripped in the framework into either a type.Float32Value or a type.Float64Value via a float32 or float64 conversion will introduce some accuracy loss in the floating point value.

Floating point numbers are compared using (big.Float).Cmp() in the framework and in terraform-plugin-go but when Terraform core compares numbers to ensure value consistency, it uses it's JSON string representation. Essentially, two numbers are considered the same in Terraform core only when they write the same JSON.

There are a certain subset of numbers where a conversion from the 512-bit precision big.Float object to either a float64 or float32 value does not result in any accuracy loss (they have the exact same binary representation). These numbers will be considered equal when compared using (big.Float).Cmp() but will write different JSON strings such that Terraform core will consider them different numbers.

The number has to meet the following conditions to run into this error (first two points from https://stackoverflow.com/a/67145511):

  • The number must be rational (can be expressed as a fraction of integers)
  • The denominator of such fraction must be a power of two (1, 2, 4, 8, etc.)
  • The number in Terraform config must must write to JSON with more precision than it's float64/float32 equivalent

This issue affects both types.Float32 and types.Float64

Potential Solutions:

To fix this issue, we would likely need to change the number comparison logic in both the framework and terraform-plugin-go to ensure that the 512-bit precision big.Float object is sent to Terraform core during the ReadResource, ReadDatasource and ApplyResourceChange RPCs as well as modifying the logic during PlanResourceChange RPC to send the correct value during planning

SBGoods avatar Jul 08 '24 20:07 SBGoods