terraform-plugin-framework
terraform-plugin-framework copied to clipboard
Provider produced invalid plan error when planning certain high-precision floating point numbers
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