Django scientific notation input validation - python

I have the following fields on my model:
class Range(models.Model):
optimal_low = models.DecimalField(max_digits=30, decimal_places=8)
optimal_high = models.DecimalField(max_digits=30, decimal_places=8)
And here's how I bring them into the form (because the form's primary object is not this model, I just need the fields, and don't want to duplicate the max_digits and decimal_places.
class ReadingMappingForm(forms.ModelForm):
optimal_low = Range._meta.get_field('optimal_low').formfield()
optimal_high = Range._meta.get_field('optimal_high').formfield()
It seems django allows entering of decimals in scientific notation out of the box, but there's a glitch above a certain threshold.
In the form, if I input 1.5E9 it works fine, and the value gets saved as 1500000000.00000000 (Here's an online scientific notation calculator).
However if I input 1.5E10 it says:
Ensure that there are no more than 8 decimal places.
Which is wrong, because I'm not adding any decimal places. In fact, if I enter 1.5E10 in normal notation, even with the 8 decimal places added, i.e. 15000000000.00000000 it works fine.
So I think something is not working correctly under the hood...
EDIT
I tested the field in the console, and it errors there:
from django.forms import DecimalField
>>> f = DecimalField(max_digits=30, decimal_places=8)
>>> f.clean('1.5E9')
Decimal('1.5E+9')
>>> f.clean('1.5E10')
Traceback (most recent call last):
File "myproject/env/lib/python3.5/site-packages/django/core/management/commands/shell.py", line 69, in handle
self.run_shell(shell=options['interface'])
File "myproject/env/lib/python3.5/site-packages/django/core/management/commands/shell.py", line 61, in run_shell
raise ImportError
ImportError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "myproject/env/lib/python3.5/site-packages/django/forms/fields.py", line 168, in clean
self.run_validators(value)
File "myproject/env/lib/python3.5/site-packages/django/forms/fields.py", line 157, in run_validators
raise ValidationError(errors)
django.core.exceptions.ValidationError: ['Ensure that there are no more than 8 decimal places.']

It does seem to be bug in DecimalValidator with the following explanation
Number that you stated 1.5E10 is parsed as object with properties _int '15' and _exp 9
In DecimalValidator __call__ method number of decimals is calculated as
decimals = abs(exponent)
which than furthermore triggers the following
if self.decimal_places is not None and decimals > self.decimal_places:
raise ValidationError(
self.messages['max_decimal_places'],
code='max_decimal_places',
params={'max': self.decimal_places},
)
Seems that fix for following would be something like
if exponent < 0:
decimal = abs(exponent)
else:
decimal = 0
Fix for version 2.0 looks like following
# If the absolute value of the negative exponent is larger than the
# number of digits, then it's the same as the number of digits,
# because it'll consume all of the digits in digit_tuple and then
# add abs(exponent) - len(digit_tuple) leading zeros after the
# decimal point.
if abs(exponent) > len(digit_tuple):
digits = decimals = abs(exponent)
else:
digits = len(digit_tuple)
decimals = abs(exponent)

This is a known bug which is fixed, but only in django 2.0 as far as I can tell.

Related

Reading a single number from another file using python

I am reading a number from a file p2.txt. This file contrains only 1 number which is an integer lets say 10.
test_file = open('p2.txt', 'r')
test_lines = test_file.readlines()
test_file.close()
ferNum= test_lines[0]
print int(ferNum)
when however, I am getting an error
print int(ferNum)
ValueError: invalid literal for int() with base 10: '1.100000000000000000e+01\n'
I can see that it is considering it just as a line. How can I parse that number to a variable? any suggestions? regards
The problem is that even though the value of the number is an integer (11) it is represented in scientific notation so you'd have to read it as a float first.
>>> float('1.100000000000000000e+01\n')
11.0
>>> int('1.100000000000000000e+01\n')
Traceback (most recent call last):
File "<pyshell#4>", line 1, in <module>
int('1.100000000000000000e+01\n')
ValueError: invalid literal for int() with base 10: '1.100000000000000000e+01\n'
You can of course convert first to a float then to an int after that.
>>> int(float('1.100000000000000000e+01\n'))
11

2's compliment of a Byte in python

I am working on the following python code:
import wave
from bitstring import BitArray
w = wave.open('file.wav','rb')
totalFrames = w.getnframes() #Total number of samples
bytesData = w.readframes(totalFrames)
binData = BitArray(bytesData)
bin2Data = (binData.bin)
The file.wav has 88200 samples at a sampling rate of 44.1KHz.
My goal is to be able to get the 2's compliment of the binary data I obtain from file.wav. 'binData.bin' gives me the binary form of the bytes (\x00\x00N\x00n\xff..) obtained through w.readframes but in a string format.
I was using this to obtain 2'scompliment:
2comp = ~(bin2Data) + 0b1
but in vain. It would show the following error:
Traceback (most recent call last):
File "speaker_bin.py", line 16, in <module>
bin2Data = ~((binData.bin)) + 0b1
TypeError: bad operand type for unary ~: 'str'
I tried int(bin2Data) to convert it but it would not work (It would not print anything at all. I guess because of the size of the data.)
What am I doing wrong?
I would really appreciate any feedback. (even a simple nudge in the right direction)
You need to use
int(binData.bin, 2)
To create an int, you can specify the base as a second parameter, otherwise it will just assume the value is in base 10. As you can see from the docs, the default base is 10, which is why you need to specify a different base other than 10
Also do the same with 0b1

Python Compiler - Using Decimal for Division of Zero?

I am currently creating a compiler and would also like to implement the division of zero. I noticed the decimal module in python and thought it could be useful. The code below shows what I am trying to get at. Is there anyway to split up the expression and check for the division of 0 for both negative and positive numbers? thanks in advance.
if input negative int/0 = -infin
if pos int/0 = infin
if 0/0 = null
ect.
The python documentation says the default behaviour is to raise an exception.
>>> import decimal
>>> D = decimal.Decimal
>>> a = D("12")
>>> b = D("0")
>>> a/b
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.4/decimal.py", line 1350, in __truediv__
return context._raise_error(DivisionByZero, 'x / 0', sign)
File "/usr/lib/python3.4/decimal.py", line 4050, in _raise_error
raise error(explanation)
decimal.DivisionByZero: x / 0
>>>
But also, instead of raising an exception:
If this signal is not trapped, returns Infinity or -Infinity with the
sign determined by the inputs to the calculation.
As of the zero by zero division, it is not mathematically defined, so I don't know what you may want to do with it. Python would return another exception:
>>> b/b
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.4/decimal.py", line 1349, in __truediv__
return context._raise_error(DivisionUndefined, '0 / 0')
File "/usr/lib/python3.4/decimal.py", line 4050, in _raise_error
raise error(explanation)
decimal.InvalidOperation: 0 / 0
>>>
and if the signal is not trapped, it returns NaN. (Might not be useful to know whether the operation was a zero divided by zero or not).

Python: limit on the accuracy of float

The code gives an error because the value of "var" is very close to zero, less than 1e-80. I tried to fix this error using "Import decimal *", but it didn't really work. Is there a way to tell Python to round a number to zero when float number is very close to zero, i.e. < 1e-50? Or any other way to fix this issue?
Thank you
CODE:
import math
H=6.6260755e-27
K=1.3807e-16
C=2.9979E+10
T=100.0
x=3.07175e-05
cst=2.0*H*H*(C**3.0)/(K*T*T*(x**6.0))
a=H*C/(K*T*x)
var=cst*math.exp(a)/((math.exp(a)-1.0)**2.0)
print var
OUTPUT:
Traceback (most recent call last):
File "test.py", line 11, in <module>
var=cst*math.exp(a)/((math.exp(a)-1.0)**2.0)
OverflowError: (34, 'Numerical result out of range')
To Kevin:
The code was edited with following lines:
from decimal import *
getcontext().prec = 7
cst=Decimal(2.0*H*H*(C**3.0)/(K*T*T*(x**6.0)))
a=Decimal(H*C/(K*T*x))
The problem is that (math.exp(a)-1.0)**2.0 is too large to hold as an intermediate result.
>>> (math.exp(a) - 1.0)**2.0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: (34, 'Result too large')
However, for the value of a you are using,
>>> math.exp(a)/(math.exp(a)-1.0) == 1.0
True
so you can essentially cancel that part of the fraction, leaving
var = cst/(math.exp(a)-1.0)
which evaluates nicely to
>>> cst/(math.exp(a)-1.0)
7.932672271698049e-186
If you aren't comfortable rewriting the formula to that extent, use the associativity of the operations to avoid the large intermediate value. The resulting product is the same.
>>> cst/(math.exp(a)-1.0)*math.exp(a)/(math.exp(a)-1.0)
7.932672271698049e-186
I solved this issue but that will work only for this particular problem, not in general. The main issue is the nature of this function:
math.exp(a)/(math.exp(a)-1.0)**2.0
which decays very rapidly.
Problem can be easily solved restricting the value of "a" (which won't make any significant change in calculation). i.e.
if a>200:
var=0.0
else:
var=cst*math.exp(a)/((math.exp(a)-1.0)**2.0)

Python cdecimal InvalidOperation

I am trying to read financial data and store it. The place I get the financial data from stores the data with incredible precision, however I am only interested in 5 figures after the decimal point. Therefore, I have decided to use t = .quantize(cdecimal.Decimal('.00001'), rounding=cdecimal.ROUND_UP) on the Decimal I create, but I keep getting an InvalidOperation exception. Why is this?
>>> import cdecimal
>>> c = cdecimal.getcontext()
>>> c.prec = 5
>>> s = '45.2091000080109'
>>> # s = '0.257585003972054' works!
>>> t = cdecimal.Decimal(s).quantize(cdecimal.Decimal('.00001'), rounding=cdecimal.ROUND_UP)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
cdecimal.InvalidOperation: [<class 'cdecimal.InvalidOperation'>]
Why is there an invalid operation here? If I change the precision to 7 (or greater), it works. If I set s to be '0.257585003972054' instead of the original value, that also works! What is going on?
Thanks!
decimal version gives a better description of the error:
Python 2.7.2+ (default, Feb 16 2012, 18:47:58)
>>> import decimal
>>> s = '45.2091000080109'
>>> decimal.getcontext().prec = 5
>>> decimal.Decimal(s).quantize(decimal.Decimal('.00001'), rounding=decimal.ROUND_UP)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.7/decimal.py", line 2464, in quantize
'quantize result has too many digits for current context')
File "/usr/lib/python2.7/decimal.py", line 3866, in _raise_error
raise error(explanation)
decimal.InvalidOperation: quantize result has too many digits for current context
>>>
Docs:
Unlike other operations, if the length of the coefficient after the
quantize operation would be greater than precision, then an
InvalidOperation is signaled. This guarantees that, unless there is an
error condition, the quantized exponent is always equal to that of the
right-hand operand.
But i must confess i don't know what this means.

Categories

Resources