Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

multipleOf incorrectly raises ValidationError for some floats #320

Closed
dalehumby opened this issue Feb 7, 2017 · 8 comments
Closed

multipleOf incorrectly raises ValidationError for some floats #320

dalehumby opened this issue Feb 7, 2017 · 8 comments

Comments

@dalehumby
Copy link

In _validators.py, the multipleOf function incorrectly raises a ValidationError for e.g. 606.67 as not a multiple of 0.01, due to floating point errors.

Our use case is using a schema to ensure that amounts (in Dollars) are in increments of 1 cent ($0.01)

Going through the code,

quotient = 606.67 / 0.01 = 60666.99999999999
failed = int(quotient) != quotient
failed = 60666 != 60666.99999999999
failed = True

One number is a multiple of another number when the quotient is an integer. However, in this case, the quotient is very close to an integer due to floating point representation of those numbers.

The solution I see is: instance is a multiple of db if quotient is close to an integer.

I'm happy to submit a pull request, and first would like to

  1. write a unit tests for this case. Where is the best place for this test?
  2. implement an isclose method similar to math.isclose in Python 3. A previous commit removed something similar. Is there a better way of doing this?
@Julian
Copy link
Member

Julian commented Feb 7, 2017

I'd recommend not using floats to recommend dollar amounts in that case. You could use a string and a simple regex fairly easily I think?

@Julian
Copy link
Member

Julian commented Feb 7, 2017

Alternatively you can deserialize your JSON using decimals as well.

@Julian
Copy link
Member

Julian commented Feb 12, 2017

Closing this out, but lemme know if you disagree.

@Julian Julian closed this as completed Feb 12, 2017
@Fufutako
Copy link

Fufutako commented Jul 13, 2017

If we use Decimal in deserialization we get an error:

jsonschema/_validators.py", line 124, in multipleOf
    quotient = instance / dB
TypeError: unsupported operand type(s) for /: 'decimal.Decimal' and 'float'

so multipleof is unusable...

This is how I did to workaround:

def multipleOf(validator, dB, instance, schema):
    if not validator.is_type(instance, "number"):
        return
    if isinstance(dB, float):
        if isinstance(instance, float):
            quotient = instance / dB
        else:
            quotient = instance / Decimal(str(dB))
        failed = int(quotient) != quotient
    else:
        failed = instance % dB

    if failed:
        yield ValidationError("%r is not a multiple of %r" % (instance, dB))

If you want I can submit a merge request. This feature is required for my purpose, however I would prefer to use the official package than a fork.

@Julian
Copy link
Member

Julian commented Jul 13, 2017 via email

@Fufutako
Copy link

Hi,

Thanks for your reply.

This is an extract from the json-schema.

'offer_items': {'items': {'maxProperties': 4,
                          'minItems': 1,
                          'properties': {'description': {'maxLength': 40,
                                                                     'type': 'string'},
                                                'price': {'multipleOf': 0.01,
                                                             'type': 'number'},
                                                'product_id': {'maxLength': 20,
                                                                      'pattern': '[0-9]+',
                                                                     'type': 'string'},
                                                 'quantity': {'type': 'integer'}},
                          'required': ['product_id',
                                            'description',
                                            'quantity',
                                            'price'],
                          'title': 'offer_item',
                          'type': 'object'}

it is stored in db and passed directly to validate. So I have no control on dB expected by the schema. How can I make my json-schema to get a Decimal for 'price' ? multipeof is a key word related to number so I don't understand how I can do that. In another hand instance when is deserialized to a float produce floating point error and when it is deserialized to a Decimal raise a TypeError.

@Julian
Copy link
Member

Julian commented Jul 13, 2017 via email

@Fufutako
Copy link

Yes... I use a json field in Postgres and sqlalchemy.JSONField to deal with. I am looking How can I do that with this package. Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants