In Python, avoid relying on float('inf') and float('nan') as these don't work on Windows with Python pre-2.6.

pull/3335/head
kenton@google.com 15 years ago
parent eef5f8396d
commit d0047c43d9
  1. 27
      python/google/protobuf/internal/generator_test.py
  2. 14
      python/google/protobuf/internal/text_format_test.py
  3. 12
      python/google/protobuf/text_format.py
  4. 20
      src/google/protobuf/compiler/python/python_generator.cc

@ -78,12 +78,27 @@ class GeneratorTest(unittest.TestCase):
def testExtremeDefaultValues(self): def testExtremeDefaultValues(self):
message = unittest_pb2.TestExtremeDefaultValues() message = unittest_pb2.TestExtremeDefaultValues()
self.assertEquals(float('inf'), message.inf_double)
self.assertEquals(float('-inf'), message.neg_inf_double) # Python pre-2.6 does not have isinf() or isnan() functions, so we have
self.assert_(message.nan_double != message.nan_double) # to provide our own.
self.assertEquals(float('inf'), message.inf_float) def isnan(val):
self.assertEquals(float('-inf'), message.neg_inf_float) # NaN is never equal to itself.
self.assert_(message.nan_float != message.nan_float) return val != val
def isinf(val):
# Infinity times zero equals NaN.
return not isnan(val) and isnan(val * 0)
self.assertTrue(isinf(message.inf_double))
self.assertTrue(message.inf_double > 0)
self.assertTrue(isinf(message.neg_inf_double))
self.assertTrue(message.neg_inf_double < 0)
self.assertTrue(isnan(message.nan_double))
self.assertTrue(isinf(message.inf_float))
self.assertTrue(message.inf_float > 0)
self.assertTrue(isinf(message.neg_inf_float))
self.assertTrue(message.neg_inf_float < 0)
self.assertTrue(isnan(message.nan_float))
def testHasDefaultValues(self): def testHasDefaultValues(self):
desc = unittest_pb2.TestAllTypes.DESCRIPTOR desc = unittest_pb2.TestAllTypes.DESCRIPTOR

@ -325,10 +325,10 @@ class TokenizerTest(unittest.TestCase):
'{', '{',
(tokenizer.ConsumeIdentifier, 'A'), (tokenizer.ConsumeIdentifier, 'A'),
':', ':',
(tokenizer.ConsumeFloat, float('inf')), (tokenizer.ConsumeFloat, text_format._INFINITY),
(tokenizer.ConsumeIdentifier, 'B'), (tokenizer.ConsumeIdentifier, 'B'),
':', ':',
(tokenizer.ConsumeFloat, float('-inf')), (tokenizer.ConsumeFloat, -text_format._INFINITY),
(tokenizer.ConsumeIdentifier, 'C'), (tokenizer.ConsumeIdentifier, 'C'),
':', ':',
(tokenizer.ConsumeBool, True), (tokenizer.ConsumeBool, True),
@ -413,6 +413,16 @@ class TokenizerTest(unittest.TestCase):
tokenizer = text_format._Tokenizer(text) tokenizer = text_format._Tokenizer(text)
self.assertRaises(text_format.ParseError, tokenizer.ConsumeBool) self.assertRaises(text_format.ParseError, tokenizer.ConsumeBool)
def testInfNan(self):
# Make sure our infinity and NaN definitions are sound.
self.assertEquals(float, type(text_format._INFINITY))
self.assertEquals(float, type(text_format._NAN))
self.assertTrue(text_format._NAN != text_format._NAN)
inf_times_zero = text_format._INFINITY * 0
self.assertTrue(inf_times_zero != inf_times_zero)
self.assertTrue(text_format._INFINITY > 0)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

@ -43,6 +43,12 @@ __all__ = [ 'MessageToString', 'PrintMessage', 'PrintField',
'PrintFieldValue', 'Merge' ] 'PrintFieldValue', 'Merge' ]
# Infinity and NaN are not explicitly supported by Python pre-2.6, and
# float('inf') does not work on Windows (pre-2.6).
_INFINITY = float('1e10000')
_NAN = _INFINITY * 0
class ParseError(Exception): class ParseError(Exception):
"""Thrown in case of ASCII parsing error.""" """Thrown in case of ASCII parsing error."""
@ -478,12 +484,12 @@ class _Tokenizer(object):
if re.match(self._FLOAT_INFINITY, text): if re.match(self._FLOAT_INFINITY, text):
self.NextToken() self.NextToken()
if text.startswith('-'): if text.startswith('-'):
return float('-inf') return -_INFINITY
return float('inf') return _INFINITY
if re.match(self._FLOAT_NAN, text): if re.match(self._FLOAT_NAN, text):
self.NextToken() self.NextToken()
return float('nan') return _NAN
try: try:
result = float(text) result = float(text)

@ -168,11 +168,15 @@ string StringifyDefaultValue(const FieldDescriptor& field) {
case FieldDescriptor::CPPTYPE_DOUBLE: { case FieldDescriptor::CPPTYPE_DOUBLE: {
double value = field.default_value_double(); double value = field.default_value_double();
if (value == numeric_limits<double>::infinity()) { if (value == numeric_limits<double>::infinity()) {
return "float('inf')"; // Python pre-2.6 on Windows does not parse "inf" correctly. However,
// parsing a number that is too big for a double will return infinity.
return "float('1e10000')";
} else if (value == -numeric_limits<double>::infinity()) { } else if (value == -numeric_limits<double>::infinity()) {
return "float('-inf')"; // See above.
return "float('-1e10000')";
} else if (value != value) { } else if (value != value) {
return "float('nan')"; // infinity * 0 = nan
return "(float('1e10000') * 0)";
} else { } else {
return SimpleDtoa(value); return SimpleDtoa(value);
} }
@ -180,11 +184,15 @@ string StringifyDefaultValue(const FieldDescriptor& field) {
case FieldDescriptor::CPPTYPE_FLOAT: { case FieldDescriptor::CPPTYPE_FLOAT: {
float value = field.default_value_float(); float value = field.default_value_float();
if (value == numeric_limits<float>::infinity()) { if (value == numeric_limits<float>::infinity()) {
return "float('inf')"; // Python pre-2.6 on Windows does not parse "inf" correctly. However,
// parsing a number that is too big for a double will return infinity.
return "float('1e10000')";
} else if (value == -numeric_limits<float>::infinity()) { } else if (value == -numeric_limits<float>::infinity()) {
return "float('-inf')"; // See above.
return "float('-1e10000')";
} else if (value != value) { } else if (value != value) {
return "float('nan')"; // infinity - infinity = nan
return "(float('1e10000') - float('1e10000'))";
} else { } else {
return SimpleFtoa(value); return SimpleFtoa(value);
} }

Loading…
Cancel
Save