Use math/big.Float instead of float64 to prevent rounding errors
diff --git a/schema.go b/schema.go index f1fbde3..e00e667 100644 --- a/schema.go +++ b/schema.go
@@ -28,11 +28,11 @@ import ( "errors" + "github.com/xeipuuv/gojsonreference" + "math/big" "reflect" "regexp" "text/template" - - "github.com/xeipuuv/gojsonreference" ) var ( @@ -482,7 +482,7 @@ }, )) } - if *multipleOfValue <= 0 { + if multipleOfValue.Cmp(big.NewFloat(0)) <= 0 { return errors.New(formatErrorDescription( Locale.GreaterThanZero(), ErrorDetails{"number": KEY_MULTIPLE_OF}, @@ -550,7 +550,7 @@ } if currentSchema.minimum != nil && currentSchema.maximum != nil { - if *currentSchema.minimum > *currentSchema.maximum { + if currentSchema.minimum.Cmp(currentSchema.maximum) == 1 { return errors.New(formatErrorDescription( Locale.CannotBeGT(), ErrorDetails{"x": KEY_MINIMUM, "y": KEY_MAXIMUM},
diff --git a/schema_test.go b/schema_test.go index 9c40e71..5a475e4 100644 --- a/schema_test.go +++ b/schema_test.go
@@ -30,6 +30,7 @@ "fmt" "io" "io/ioutil" + "math/big" "net/http" "os" "reflect" @@ -58,12 +59,16 @@ func (a evenNumberFormatChecker) IsFormat(input interface{}) bool { - asFloat64, ok := input.(float64) + asBigFloat, ok := input.(*big.Float) + if ok == false { return false } - return int(asFloat64)%2 == 0 + q := new(big.Float).SetFloat64(2) + q.Quo(asBigFloat, q) + + return q.IsInt() } func (a invoiceFormatChecker) IsFormat(input interface{}) bool {
diff --git a/subSchema.go b/subSchema.go index 9961d92..b4a20ae 100644 --- a/subSchema.go +++ b/subSchema.go
@@ -28,6 +28,7 @@ import ( "errors" + "math/big" "regexp" "strings" @@ -98,10 +99,10 @@ propertiesChildren []*subSchema // validation : number / integer - multipleOf *float64 - maximum *float64 + multipleOf *big.Float + maximum *big.Float exclusiveMaximum bool - minimum *float64 + minimum *big.Float exclusiveMinimum bool // validation : string
diff --git a/utils.go b/utils.go index 26cf75e..98e0da0 100644 --- a/utils.go +++ b/utils.go
@@ -29,6 +29,7 @@ "encoding/json" "fmt" "math" + "math/big" "reflect" "strconv" ) @@ -138,15 +139,13 @@ return nil } -func mustBeNumber(what interface{}) *float64 { +func mustBeNumber(what interface{}) *big.Float { if isJsonNumber(what) { - number := what.(json.Number) - float64Value, err := number.Float64() - - if err == nil { - return &float64Value + float64Value, success := new(big.Float).SetString(string(number)) + if success { + return float64Value } else { return nil }
diff --git a/validation.go b/validation.go index 9afea25..5d0422c 100644 --- a/validation.go +++ b/validation.go
@@ -27,6 +27,7 @@ import ( "encoding/json" + "math/big" "reflect" "regexp" "strconv" @@ -759,17 +760,17 @@ } number := value.(json.Number) - float64Value, _ := number.Float64() + float64Value, _ := new(big.Float).SetString(string(number)) // multipleOf: if currentSubSchema.multipleOf != nil { - if !isFloat64AnInteger(float64Value / *currentSubSchema.multipleOf) { + if q := new(big.Float).Quo(float64Value, currentSubSchema.multipleOf); !q.IsInt() { result.addError( new(MultipleOfError), context, resultErrorFormatJsonNumber(number), - ErrorDetails{"multiple": *currentSubSchema.multipleOf}, + ErrorDetails{"multiple": currentSubSchema.multipleOf}, ) } } @@ -777,24 +778,24 @@ //maximum & exclusiveMaximum: if currentSubSchema.maximum != nil { if currentSubSchema.exclusiveMaximum { - if float64Value >= *currentSubSchema.maximum { + if float64Value.Cmp(currentSubSchema.maximum) >= 0 { result.addError( new(NumberLTError), context, resultErrorFormatJsonNumber(number), ErrorDetails{ - "max": resultErrorFormatNumber(*currentSubSchema.maximum), + "max": currentSubSchema.maximum, }, ) } } else { - if float64Value > *currentSubSchema.maximum { + if float64Value.Cmp(currentSubSchema.maximum) == 1 { result.addError( new(NumberLTEError), context, resultErrorFormatJsonNumber(number), ErrorDetails{ - "max": resultErrorFormatNumber(*currentSubSchema.maximum), + "max": currentSubSchema.maximum, }, ) } @@ -804,24 +805,25 @@ //minimum & exclusiveMinimum: if currentSubSchema.minimum != nil { if currentSubSchema.exclusiveMinimum { - if float64Value <= *currentSubSchema.minimum { + if float64Value.Cmp(currentSubSchema.minimum) <= 0 { + // if float64Value <= *currentSubSchema.minimum { result.addError( new(NumberGTError), context, resultErrorFormatJsonNumber(number), ErrorDetails{ - "min": resultErrorFormatNumber(*currentSubSchema.minimum), + "min": currentSubSchema.minimum, }, ) } } else { - if float64Value < *currentSubSchema.minimum { + if float64Value.Cmp(currentSubSchema.minimum) == -1 { result.addError( new(NumberGTEError), context, resultErrorFormatJsonNumber(number), ErrorDetails{ - "min": resultErrorFormatNumber(*currentSubSchema.minimum), + "min": currentSubSchema.minimum, }, ) }