Wii VC Round-to-Zero: Difference between revisions

Jump to navigation Jump to search
m
no edit summary
(fix grammar and remove repeated info)
mNo edit summary
Line 3: Line 3:
'''Wii VC Round-to-Zero''' is an emulation inaccuracy in the [[Virtual Console|Wii Virtual Console]] (Wii VC) version of ''[[Super Mario 64]]''.
'''Wii VC Round-to-Zero''' is an emulation inaccuracy in the [[Virtual Console|Wii Virtual Console]] (Wii VC) version of ''[[Super Mario 64]]''.


When converting a [[wikipedia:Double-precision floating-point format|double-precision floating point number]] (a ''double'') to a [[wikipedia:Single-precision floating-point format|single-precision floating point number]] (a ''float''), some doubles will have to be [[wikipedia:Rounding|rounded]] to a nearby float. There is a discrepancy in the rounding mode used:
When converting a {{w|Double-precision floating-point format|double-precision floating point number}} (a ''double'') to a {{w|Single-precision floating-point format|single-precision floating point number}} (a ''float''), some doubles will have to be {{w|Rounding|rounded}} to a nearby float. There is a discrepancy in the rounding mode used:
*The Nintendo 64 typically uses the [[wikipedia:Rounding#Rounding to the nearest integer|''round-to-nearest'']] mode. This means that if the double is not exactly equal to a float, it will round up or down to the float that is closest in value to the original number. This behavior is accurately emulated by most emulators and the Wii U VC.
*The Nintendo 64 typically uses the {{w|Rounding#Rounding to the nearest integer|''round-to-nearest''}} mode. This means that if the double is not exactly equal to a float, it will round up or down to the float that is closest in value to the original number. This behavior is accurately emulated by most emulators and the Wii U VC.
*The Wii VC instead effectively uses a [[wikipedia:Rounding#Rounding toward zero|''round-toward-zero'']] mode. This mode rounds all positive doubles down, and all negative doubles up, to the nearest float.
*The Wii VC instead effectively uses a {{w|Rounding#Rounding toward zero|''round-toward-zero''}} mode. This mode rounds all positive doubles down, and all negative doubles up, to the nearest float.


While the programmers did not intentionally use doubles very often (favoring single-precision floats instead), they occasionally used them accidentally, causing extra double-to-single conversions in many places in the game's source code. The rounding involved creates discrepancies in the Wii VC version of the game, such as slightly different [[wikipedia:Normal (geometry)|surface normals]], which build up over time throughout gameplay. For this reason, [[TAS]]es produced for either N64 or Wii VC cannot generally be played back on the other platform without desync.
While the programmers did not intentionally use doubles very often (favoring single-precision floats instead), they occasionally used them accidentally, causing extra double-to-single conversions in many places in the game's source code. The rounding involved creates discrepancies in the Wii VC version of the game, such as slightly different {{w|Normal (geometry)|surface normals}}, which build up over time throughout gameplay. For this reason, [[TAS]]es produced for either N64 or Wii VC cannot generally be played back on the other platform without desync.


Technically, the Wii VC version of ''Super Mario 64'' does set the Wii's CPU to round-to-nearest. However, storing a double to the Wii's floating point registers and reading it back as a single is [[wikipedia:Undefined behavior|undefined behavior]]. In practice, doing so ends up discarding the lower bits of the [[wikipedia:Significand|mantissa]]&mdash;a [[wikipedia:Truncation|truncation]], which rounds toward zero. (The opposite situation, storing a single and reading it back as a double, is supported and converted correctly.)<ref name="dolphin">[https://dolphin-emu.org/blog/2018/07/06/dolphin-progress-report-june-2018 "Dolphin Progress Report: June 2018"]</ref>
Technically, the Wii VC version of ''Super Mario 64'' does set the Wii's CPU to round-to-nearest. However, storing a double to the Wii's floating point registers and reading it back as a single is {{w|undefined behavior}}. In practice, doing so ends up discarding the lower bits of the {{w|Significand|mantissa}}&mdash;a {{w|truncation}}, which rounds toward zero. (The opposite situation, storing a single and reading it back as a double, is supported and converted correctly.)<ref name="dolphin">[https://dolphin-emu.org/blog/2018/07/06/dolphin-progress-report-june-2018 "Dolphin Progress Report: June 2018"]</ref>


==Platform drift==
==Platform drift==
In [[Bowser in the Fire Sea]], round-to-zero causes a bug known as '''Wii VC platform drift''', where the oscillating platforms that sink into lava will slowly rise up to the height of the [[wikipedia:Origin (mathematics)|origin]].
In [[Bowser in the Fire Sea]], round-to-zero causes a bug known as '''Wii VC platform drift''', where the oscillating platforms that sink into lava will slowly rise up to the height of the {{w|Origin (mathematics)|origin}}.


This code controls the oscillation of these platforms. The variable <code>y</code> represents the platform's height.
This code controls the oscillation of these platforms. The variable <code>y</code> represents the platform's height.
Line 21: Line 21:
</syntaxhighlight>
</syntaxhighlight>


Here, <code>y</code> and <code>sins(t)</code> are both single-precision floats. However, in C, the [[wikipedia:Literal (computer programming)|numeric literal]] <code>0.58</code> is a double since it lacks a trailing <code>f</code>, as in <code>0.58f</code>. This causes the expression <code>sins(t) * 0.58</code> to evaluate to a double, which is then converted back to a float for the subtraction-assignment to <code>y</code>.
Here, <code>y</code> and <code>sins(t)</code> are both single-precision floats. However, in C, the {{w|Literal (computer programming)|numeric literal}} <code>0.58</code> is a double since it lacks a trailing <code>f</code>, as in <code>0.58f</code>. This causes the expression <code>sins(t) * 0.58</code> to evaluate to a double, which is then converted back to a float for the subtraction-assignment to <code>y</code>.


Using the N64's rounding mode, <code>y</code> will repeatedly return to its original value and continue oscillating with fixed maximum height. Even though each double-to-single conversion produces a small amount of error, this error balances out over the course of the platform's cycle. On the Wii VC, however, the error always accumulates in the same direction (upward if the platform is below <var>y</var> = 0, and downward if the platform is above <var>y</var> = 0), and so the platform slowly drifts in the vertical direction.
Using the N64's rounding mode, <code>y</code> will repeatedly return to its original value and continue oscillating with fixed maximum height. Even though each double-to-single conversion produces a small amount of error, this error balances out over the course of the platform's cycle. On the Wii VC, however, the error always accumulates in the same direction (upward if the platform is below <var>y</var> = 0, and downward if the platform is above <var>y</var> = 0), and so the platform slowly drifts in the vertical direction.
Line 34: Line 34:
:<math>\frac{127}{256} \times \frac{1}{4096}\ \frac{\mathrm{units}}{\mathrm{frames}} \times \left(30\ \frac{\mathrm{frames}}{\mathrm{s}} \times 60\ \frac{\mathrm{s}}{\mathrm{min}} \times 60\ \frac{\mathrm{min}}{\mathrm{h}}\right) \approx 13.0806\ \frac{\mathrm{units}}{\mathrm{h}}</math>
:<math>\frac{127}{256} \times \frac{1}{4096}\ \frac{\mathrm{units}}{\mathrm{frames}} \times \left(30\ \frac{\mathrm{frames}}{\mathrm{s}} \times 60\ \frac{\mathrm{s}}{\mathrm{min}} \times 60\ \frac{\mathrm{min}}{\mathrm{h}}\right) \approx 13.0806\ \frac{\mathrm{units}}{\mathrm{h}}</math>


The floating-point numbers are distributed non-uniformly; the gap between floats doubles after each [[wikipedia:Power of two|power of two]], so their number line is denser closer to zero. Because the platform drifts toward zero, the error factor (initially <math display="inline">\frac{1}{4096}</math>) is halved after certain thresholds, and so the platform's net speed is also halved. First, above <var>y</var> = &minus;2048, it drops to about 6.54 units per hour. It continues to halve in net speed as it passes through <var>y</var> = &minus;1024, <var>y</var> = &minus;512, and all other <math display="inline">\left|y\right| = 2^n</math> boundaries, until it stops around <var>y</var> = 0.
The floating-point numbers are distributed non-uniformly; the gap between floats doubles after each {{w|power of two}}, so their number line is denser closer to zero. Because the platform drifts toward zero, the error factor (initially <math display="inline">\frac{1}{4096}</math>) is halved after certain thresholds, and so the platform's net speed is also halved. First, above <var>y</var> = &minus;2048, it drops to about 6.54 units per hour. It continues to halve in net speed as it passes through <var>y</var> = &minus;1024, <var>y</var> = &minus;512, and all other <math display="inline">\left|y\right| = 2^n</math> boundaries, until it stops around <var>y</var> = 0.


Each time the platform oscillates through one of these boundaries, its behavior is more complicated. Throughout its cycle, the platform is sometimes above <math display="inline">\left|y\right| = 2^n</math> and sometimes below. The overall speed decrease as the platform's average height passes through the boundary is represented by the [[wikipedia:Differential equation|differential equation]]
Each time the platform oscillates through one of these boundaries, its behavior is more complicated. Throughout its cycle, the platform is sometimes above <math display="inline">\left|y\right| = 2^n</math> and sometimes below. The overall speed decrease as the platform's average height passes through the boundary is represented by the {{w|differential equation}}
:<math>v_y = \frac{dy}{dt} = \arccos\left(y\right)</math>
:<math>v_y = \frac{dy}{dt} = \arccos\left(y\right)</math>
The platform suddenly slows down quickly, then at a slower rate before it decelerates faster again, and finally, suddenly the platform's acceleration completely stops, the speed settling at a constant, half of what it started with.
The platform suddenly slows down quickly, then at a slower rate before it decelerates faster again, and finally, suddenly the platform's acceleration completely stops, the speed settling at a constant, half of what it started with.
Line 49: Line 49:


==Scope==
==Scope==
The bug does not occur in any later Wii VC releases, nor in the Wii U VC or in any mainstream Nintendo 64 emulators. At the time of its discovery, the bug did not occur on the [[wikipedia:Dolphin (emulator)|Dolphin]] emulator, but was corrected in 2018 to match the behavior of the Wii hardware.<ref name="dolphin" />
The bug does not occur in any later Wii VC releases, nor in the Wii U VC or in any mainstream Nintendo 64 emulators. At the time of its discovery, the bug did not occur on the {{w|Dolphin (emulator)|Dolphin}} emulator, but was corrected in 2018 to match the behavior of the Wii hardware.<ref name="dolphin" />


No objects other than the sinking platforms are known to have similarly exploitable behavior. (However, as said, the rounding inaccuracy does cause other discrepancies between the versions of the game.)
No objects other than the sinking platforms are known to have similarly exploitable behavior. (However, as said, the rounding inaccuracy does cause other discrepancies between the versions of the game.)
Line 57: Line 57:
==References==
==References==
<references />
<references />
 
{{Glitches}}
<!-- Category -->
[[Category:Glitches]]
[[Category:Glitches]]
{{Glitches}}
confirmed_editors
135

edits

Navigation menu