mojira.dev
MC-171881

Cannot create negative zero in NBT

The bug

It is not possible to create -0.0f/-0.0d in NBT, because 0.0f/0.0d is created instead. This prevents objects in memory containing negative zero from being accurately serialized.

It was possible to create negative zero before 19w38b. However, it became impossible to create them since 19w39a due to NBT caching.

Furthermore, if the Named Binary Tag specification (formerly at http://www.minecraft.net/docs/NBT.txt) is still valid, this behavior would violate the Payload specification for TAG_Float/TAG_Double: A floating point value (32 bits, big endian, IEEE 754-2008, binary32) / A floating point value (64 bits, big endian, IEEE 754-2008, binary64).

How to reproduce

Float tags

/data modify storage mc-171881: float set value -0.0f
/data get storage mc-171881: float
Expected behavior
Storage mc-171881: has the following contents: -0.0f
Actual behavior
Storage mc-171881: has the following contents: 0.0f

Double tags

/data modify storage mc-171881: double set value -0.0d
/data get storage mc-171881: double
Expected behavior
Storage mc-171881: has the following contents: -0.0d
Actual behavior
Storage mc-171881: has the following contents: 0.0d

Code analysis

Since +0.0 and -0.0 are considered identical in IEEE 754, ZERO (the cached 0.0) is returned when value is -0.0.

// net.minecraft.nbt.FloatTag
public static FloatTag valueOf(float value) {
    return value == 0.0f ? ZERO : new FloatTag(value);
}

// net.minecraft.nbt.DoubleTag
public static DoubleTag valueOf(double value) {
    return value == 0.0d ? ZERO : new DoubleTag(value);
}

Comments 3

In what scenario would it be useful to have a distinction between 0.0 and -0.0? I'm partially referencing tickets like MC-153454, which have been resolved as invalid.

Though I'm not sure about the usefulness of the distinction it's unnatural that the floating-point representation used in NBT doesn't match that used in Java. I want to clarify the specification of negative zero in NBT.

MC-153454 seems to be someone misunderstanding that the XYZ line of the debug screen corresponds to the player's coordinates as an entity (which logically starts with -0 when between 0 and -1) and not the coordinates of a block (which would not make sense to start with -0). However, there's still an underlying bug in the F3 menu related to a true -0 in the XYZ line, so I've added a comment to that issue.

With that in mind, this issue is different because it's about conformance to IEEE floating point specifications rather than about whether something is possible or makes logical sense for any specific data tag.

Regarding usefulness, while it may not be a useful distinction in vanilla, it'd be quite useful for datapacks to be able to set an otherwise impossible NBT value that doesn't cause immediate errors like Infinityd or -Infinityd (possible via setting the value to 1.79769313486232E+308d). This could potentially be used as an error condition or something similar if it's possible to test for 0.0 vs -0.0

Edit: Also, I apparently have no idea how to properly link an issue.

intsuc

(Unassigned)

Confirmed

Commands

nbt

1.15.2, 20w06a, 20w07a, 20w08a, 20w09a, ..., 24w33a, 1.21.4, 25w04a, 1.21.5, 25w16a

Retrieved