pingcap / pingcap/tidb

Inconsistency in ATAN2(y, x) results between TiDB and TiKV when x is signed zero

Open
#66,270 2 comments 0 reactions 0 assignees View on GitHub
contribution may-affects-7.1 may-affects-7.5 may-affects-8.1 may-affects-8.5 severity/major sig/execution type/bug
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.