mojira.dev
MC-259032

/data produces a positive result for low negative numbers

When you apply a /data get command to a value that is smaller than -2147483648 (-231, the lowest int value), it returns a result of 2147483647 (231-1, the highest int value).

Explanation:

For the result of the /data get command, the following function is used to convert into an integer:

public static int floor(double x) {
    int i = (int) x;
    return x < i ? x - 1 : x;
}

The first line in the function removes the digits after the decimal point. Since the int type only provides the fixed range between -2147483648 (-231) and 2147483647 (231-1), any values above or below are reduced to this maximum or minimum, meaning that any values below -231 are converted to exactly -231.

The problem then occurres in the second line, which will, in the case of such a low number, say yes, x is smaller than -231 and try to subtract 1. Since the number is already the smallest possible, this will cause the number to underflow and wrap around to the maximum value (231-1).

Implications:

A usually good approach to determining whether a numeric tag is negative is to take its /data get result and determine whether that is negative. This approach stops working because of this bug. In fact, there is currently no simple approach that works for all numbers (except for using number to string conversion in function macros, which is much slower and tedious).

Possible fix:

Change the implementation of net.minecraft.util.Mth.floor to this:

public static int floor(double x) {
    return (int) Math.floor(x);
}

This produces the desired output and fixes the bug. For any performance considerations, it seems to be approximately as fast as the current approach, maybe slightly slower.

Steps to Reproduce:
1. Create an NBT tag with a value below -231, e.g.

/data modify storage test:test value set value -3.0e9d

2. Run /data get and store its result using /execute store:

/execute store result storage test:test result int 1 run data get storage test:test value

3. Retrieve the result

/data get storage test:test result

The result will be 2147483647 (a positive integer), while it should be a negative integer like -2147483648.

Linked issues

Attachments

Comments 6

Relates to MC-121824. This is actually expected, nothing prevents NBT from over/underflowing either, and the result is directly casted to an integer, no special code to handle other data types.

Well, not exactly. Actually, when converting a double into an int directly, Java will just return the minimum possible integer if the double is too small to fit into a int. But Minecraft uses a special method to round down doubles into ints, which returns the maximum possible integer for both too low and too high values. In my opinion it should return the maximum possible integer for too high and the minimum possible integer for too low numbers.

I can confirm this issue.

[media]

Updated the description to better explain the issue.

Can confirm in 1.20.4 Release Candidate 1

Rob23

(Unassigned)

Confirmed

Platform

Normal

Commands

1.19.3, 23w04a, 1.20 Release Candidate 1, 1.20, 23w35a, ..., 24w03a, 24w04a, 24w05a, 24w06a, 24w07a

Retrieved