diff --git a/lib/iris/fileformats/grib/_save_rules.py b/lib/iris/fileformats/grib/_save_rules.py index 908b43a6cd..d74dd61843 100644 --- a/lib/iris/fileformats/grib/_save_rules.py +++ b/lib/iris/fileformats/grib/_save_rules.py @@ -38,6 +38,26 @@ from iris.fileformats.grib import grib_phenom_translation as gptx +def fixup_int32_as_uint32(value): + """ + Workaround for use when the ECMWF GRIB API treats a signed, 4-byte + integer value as an unsigned, 4-byte integer. + + Returns the unsigned integer value which will result in the on-disk + representation corresponding to the signed, 4-byte integer value. + + """ + value = int(value) + if -0x7fffffff <= value <= 0x7fffffff: + if value < 0: + # Convert from two's-complement to sign-and-magnitude. + value = 0x80000000 - value + else: + msg = '{} out of range -2147483647 to 2147483647.'.format(value) + raise ValueError(msg) + return value + + ############################################################################### # # Constants diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_fixup_int32_as_uint32.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_fixup_int32_as_uint32.py new file mode 100644 index 0000000000..716f7d7d49 --- /dev/null +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_fixup_int32_as_uint32.py @@ -0,0 +1,54 @@ +# (C) British Crown Copyright 2014, Met Office +# +# This file is part of Iris. +# +# Iris is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Iris is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Iris. If not, see . +""" +Unit tests for `iris.fileformats.grib._save_rules.fixup_int32_as_uint32`. + +""" + +from __future__ import (absolute_import, division, print_function) + +# Import iris.tests first so that some things can be initialised before +# importing anything else. +import iris.tests as tests + +from iris.fileformats.grib._save_rules import fixup_int32_as_uint32 + + +class Test(tests.IrisTest): + def test_very_negative(self): + with self.assertRaises(ValueError): + fixup_int32_as_uint32(-0x80000000) + + def test_negative(self): + result = fixup_int32_as_uint32(-3) + self.assertEqual(result, 0x80000003) + + def test_zero(self): + result = fixup_int32_as_uint32(0) + self.assertEqual(result, 0) + + def test_positive(self): + result = fixup_int32_as_uint32(5) + self.assertEqual(result, 5) + + def test_very_positive(self): + with self.assertRaises(ValueError): + fixup_int32_as_uint32(0x80000000) + + +if __name__ == '__main__': + tests.main()