code: def checkFloatEqAndReplace(
      self, expected, actual, relative_tolerance
  ):  # pylint: disable=invalid-name
      """Recursively replaces the floats in actual with those in expected iff they are approximately equal.
  
      This is done because string equality will consider values such as 5.0999999999
      and 5.1 as not being equal, despite being extremely close.
  
      Args:
        self: googletest.TestCase
        expected: expected values
        actual: actual values
        relative_tolerance: float, relative tolerance.
      """
  
      for expected_fields, actual_fields in zip(
          expected.ListFields(), actual.ListFields()
      ):
          is_repeated = True
          expected_desc, expected_values = expected_fields
          actual_values = actual_fields[1]
          if (
              expected_desc.label
              != descriptor.FieldDescriptor.LABEL_REPEATED
          ):
              is_repeated = False
              expected_values = [expected_values]
              actual_values = [actual_values]
  
          if (
              expected_desc.type
              == descriptor.FieldDescriptor.TYPE_FLOAT
              or expected_desc.type
              == descriptor.FieldDescriptor.TYPE_DOUBLE
          ):
              for i, (x, y) in enumerate(
                  zip(expected_values, actual_values)
              ):
                  # Replace the actual value with the expected value if the test passes,
                  # otherwise leave it and let it fail in the next test so that the error
                  # message is nicely formatted
                  if isClose(x, y, relative_tolerance):
                      if is_repeated:
                          getattr(actual, actual_fields[0].name)[i] = x
                      else:
                          setattr(actual, actual_fields[0].name, x)
  
          if (
              expected_desc.type
              == descriptor.FieldDescriptor.TYPE_MESSAGE
              or expected_desc.type
              == descriptor.FieldDescriptor.TYPE_GROUP
          ):
              if (
                  expected_desc.type
                  == descriptor.FieldDescriptor.TYPE_MESSAGE
                  and expected_desc.message_type.has_options
                  and expected_desc.message_type.GetOptions().map_entry
              ):
                  # This is a map, only recurse if it has type message type.
                  if (
                      expected_desc.message_type.fields_by_number[
                          2
                      ].type
                      == descriptor.FieldDescriptor.TYPE_MESSAGE
                  ):
                      for e_v, a_v in zip(
                          iter(expected_values.values()),
                          iter(actual_values.values()),
                      ):
                          checkFloatEqAndReplace(
                              self,
                              expected=e_v,
                              actual=a_v,
                              relative_tolerance=relative_tolerance,
                          )
              else:
                  for v, a in zip(expected_values, actual_values):
                      # recursive step
                      checkFloatEqAndReplace(
                          self,
                          expected=v,
                          actual=a,
                          relative_tolerance=relative_tolerance,
                      )
  
file_path: tensorflow-upstream/tensorflow/python/util/protobuf/compare.py
imports: import collections.abc
  import difflib
  import math
  from google.protobuf import descriptor
  from google.protobuf import descriptor_pool
  from google.protobuf import message
  from google.protobuf import text_format
label_desc: Use image node_id 2 for calling a global function with example usage: checkFloatEqAndReplace(expected, actual, relative_tolerance) without return types