Inconsistency in ATAN2(y, x) results between TiDB and TiKV when x is signed zero
- Dominant language
- Go
- Stars
- 40.5k
- Forks
- 6.2k
- PR merge metrics
- PR metrics pending
Description
## Bug Report
Please answer these questions before submitting your issue. Thanks!
#### Description
There is a calculation inconsistency between the TiDB execution layer (constant folding) and the TiKV Coprocessor when handling the `ATAN2(y, x)` function with a **signed zero (`-0.0`)** as the second argument.
**Key Findings:**
1. **Signed Zero Handling**: In MySQL/TiDB, the expression `-'o'` evaluates to `-0.0`. According to the IEEE 754 standard and MySQL compatibility, `ATAN2(0, -0.0)` should return $\pi$ (approximately `3.14159...`).
2. **Execution Path Inconsistency**:
* **TiDB Server**: When the expression is composed of constants, the TiDB optimizer performs **constant folding**. It correctly evaluates `ATAN2(0, -0.0)` as a non-zero value, so `NOT ATAN2(...)` becomes `0` (False).
* **TiKV Coprocessor**: When the first argument is a column reference (forcing a pushdown), TiKV appears to lose the sign bit of the second argument, treating `-0.0` as `0.0`. Since `ATAN2(0, 0)` is `0`, `NOT 0` evaluates to `1` (True).
3. **Result**: This leads to a divergence where the same logical condition returns different results depending on whether the calculation is folded by the optimizer or pushed down to the storage engine.
**Logical Breakdown:**
- Standard: `ATAN2(0, -0.0)` $\rightarrow$ `3.14159...` $\rightarrow$ `NOT` $\rightarrow$ `0` (False)
- TiKV Bug: `ATAN2(0, -0.0)` evaluated as `ATAN2(0, 0)` $\rightarrow$ `0` $\rightarrow$ `NOT` $\rightarrow$ `1` (True)
### 1. Minimal reproduce step (Required)
```sql
CREATE TABLE t0(c0 INT);
INSERT INTO t0 VALUES (0);
CREATE VIEW v0 AS SELECT 0 AS c0 FROM t0;
SELECT v0.c0 FROM v0 WHERE (NOT ATAN2(c0, -'o'));
SELECT ref0 FROM (SELECT c0 AS ref0, (NOT ATAN2(c0, -'o')) AS ref1 FROM v0) AS s WHERE ref1;
```
### 2. What did you expect to see? (Required)
```sql
mysql> SELECT t0.c0 FROM t0 WHERE (NOT ATAN2(0, -'o'));
Empty set, 1 warning (0.00 sec)
mysql> SELECT ref0 FROM (SELECT t0.c0 AS ref0, (NOT ATAN2(c0, -'o')) AS ref1 FROM t0) AS s WHERE ref1;
Empty set, 1 warning (0.00 sec)
mysql> select version();
+-----------+
| version() |
+-----------+
| 9.6.0 |
+-----------+
1 row in set (0.00 sec)
```
### 3. What did you see instead (Required)
```sql
mysql> SELECT t0.c0 FROM t0 WHERE (NOT ATAN2(0, -'o'));
Empty set, 1 warning (0.00 sec)
mysql> SELECT ref0 FROM (SELECT t0.c0 AS ref0, (NOT ATAN2(c0, -'o')) AS ref1 FROM t0) AS s WHERE ref1;
+------+
| ref0 |
+------+
| 0 |
+------+
1 row in set, 1 warning (0.00 sec)
mysql> select version();
+--------------------+
| version() |
+--------------------+
| 8.0.11-TiDB-v8.5.5 |
+--------------------+
1 row in set (0.00 sec)
```
### 4. What is your TiDB version? (Required)
```sql
mysql> select tidb_version();
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| tidb_version() |
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Release Version: v8.5.5
Edition: Community
Git Commit Hash: 1fa258b833ff113883beeba40bc130be7ce66610
Git Branch: HEAD
UTC Build Time: 2026-01-14 22:20:57
GoVersion: go1.25.5
Race Enabled: false
Check Table Before Drop: false
Store: unistore |
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
```
Contributor guide
Research direction
Start by running the supplied SQL reproduction and compare the constant-folding path with the TiKV coprocessor path for ATAN2 and signed zero. Trace where the negative sign of -0.0 is lost; done means both execution paths return the same result as the expected MySQL behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go, sql
- Domain
- databases, distributed-systems
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100